iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Software Development

Laravel專案練習-寶可夢管理系統系列 第 24

Day24:寶可夢專案-學習寫測試-featureTest實作

  • 分享至 

  • xImage
  •  
  • 基本步驟

    建立測試用的database

    • 為什麼要建立測試用的資料庫?
      • 隔離測試和生產數據:

        總不希望妳生成的假數據存到真的資料庫裡吧。

      • 提供一致的測試環境:
        使用測試資料庫,我們可以在每次測試之前重置其狀態,確保測試的一致性。

      • 提高測試速度:
        生產數據庫可能包含大量的數據,而測試資料庫可以只包含測試所需的最小數據集。這意味著在測試資料庫上執行測試操作(如資料查詢、插入或更新)通常會比在生產數據庫上更快。也就是說如果我今天只是要測試取出來的資料是否有符合一定格式含有符合預期的狀態碼,我不用一次返回一大包。

    1. 建立測試用的數據庫
      • 首先,你需要在MySQL或你使用的任何數據庫系統中建立一個新的數據庫,例如命名為**my_testing_database**。
    2. 更新環境設定
      • 在你的Laravel專案中,你可能已經有一個 .env 文件,它包含你的本地開發環境的數據庫設置。為了測試,你可以建立一個 .env.testing 文件,裡面包含測試數據庫的連接信息,例如:

        
        DB_CONNECTION=mysql 
        DB_HOST=127.0.0.1 
        DB_PORT=3306 
        DB_DATABASE=my_testing_database 
        DB_USERNAME=root 
        DB_PASSWORD=your_actual_password
        
        

        phpunit.xml 文件中將這行註解去掉

        <env name="DB_DATABASE" value="my_testing_database"/>
        
        1. 執行遷移以建立表結構
          使用下列指令執行遷移,這將會在測試數據庫中建立所有必要的表結構:

          
          php artisan migrate --env=testing
          
          
        2. 選填:執行資料填充器
          如果需要填充測試數據,可以運行:

          
          php artisan db:seed --env=testing
          
          

          編寫和運行測試
          現在,當你運行測試時,它應該會使用你為測試設置的數據庫,而不是你的主要開發數據庫。

    我的實作

    php artisan make:test NameOfYourTest
    
    • 這裡我先針對我的寶可夢新增這隻API去寫featureTest(以下是正確的情況)

      <?php
      
      namespace Tests\Feature\PokemonStoreTest;
      
      use Illuminate\Foundation\Testing\RefreshDatabase;
      use Illuminate\Support\Arr;
      use Tests\TestCase;
      
      class HappyPathTest extends TestCase
      {
          use RefreshDatabase;
          /**
           * A basic feature test example.
           */
      
          //  Happy Path
          // 確認傳入正確的參數後:
          //  1.是否有正確的狀態馬, 2.是否有存入資料庫
      
          public function testPokemonStore()
          {
              // 模擬要傳入的參數
              $mockedData = createMockData();
      
              // 保存隨機生成的技能ID,以便於後面驗證
              $expectedSkills = $mockedData['skills']->pluck('id')->toArray();
      
              // 將模型實例傳入,以便於在函式裡面取得id
              $data = getMockData($mockedData);
      
              ......後續邏輯
          }
      }
      

      首先去創建資料,取得資料,分別用到createMockData()和getMockData()。

      這裡我加了一行 use RefreshDatabase,目的是為了在每次測試之後都會回滾數據庫的變動,這樣可以確保每次測試的環境都是一致的。

    • createMockData():

      我創建模擬資料的函式(這裡我是寫在輔助函數)

      <?php
      
      use App\Models\Ability;
      use App\Models\Nature;
      use App\Models\Race;
      use App\Models\Skill;
      
      function createMockData($evolution_level = 15, $skillNumber = 4)
          {
              // 創造數據返回id
              $race = Race::factory()->create(['evolution_level' => $evolution_level]);
              // $user = User::factory()->create(['role' => $role]);
              $nature = Nature::factory()->create();
              $ability = Ability::factory()->create();
              $skills = Skill::factory($skillNumber)->create();
      
              $race->skills()->attach($skills->pluck('id'));
      
              return compact('race', 'nature', 'ability', 'skills');
          }
      

      這裡我在參數的部分可以讓使用函數的地方可以選擇是否要更改進化等級及技能,

      這部分是為了例外測試用的,比如說我輸入技能數量超過4的話應該要噴錯誤。

    • getMockData():

      前面在於創建資料,拿到的是資料的物件,這裡是從物件裡面取得實際的值後,以陣列返回

      <?php
      
      use App\Models\Pokemon;
      
      function getMockData($mockData, $overrides = [])
          {
      
              // $mockData = $this->createMockData($role);
      
              $pokemon = Pokemon::factory()->make();
              // 接收數據  組裝
      
              $data = [
                  "name" => $pokemon->name,
                  "race_id" => $mockData['race']->id,
                  "skills" => $mockData['skills']->pluck('id')->toArray(),
                  "ability_id" => $mockData['ability']->id,
                  "nature_id" => $mockData['nature']->id,
                  "level" => $mockData['race']->evolution_level - 1,
      
              ];
      
              // 用這樣的方法可以彈性的讓使用者輸入參數去修改陣列裡的數值
              return array_merge($data, $overrides);
          }
      

    在取得資料後就拿這個創建的資料去實際發請球到我的API

    • 確認狀態碼

      //發送請求
              $response = $this->post('api/pokemons', $data)
                  ->assertStatus(201); // Assuming 201 means created
      
      
      

      這laravel 測試class的底層有提供一些發請求的method(像我這裡用的是post),將endpoint打上,還有剛剛創建的資料,然後輸入我的預期的狀態碼,如果實際上返回的有符合預期,就不會噴錯誤。

    • 確認是否新增到資料庫

         // 檢查數據庫是否有相對應的數據
              $this->assertDatabaseHas('pokemons', Arr::except($data, ['skills']));
      
              // 獲取剛剛創建的 Pokemon
              $pokemonStored = \App\Models\Pokemon::where('name', $data['name'])->firstOrFail();
      
              // 檢查存儲的技能是否與預期的技能相匹配
      
              $this->assertEqualsCanonicalizing($expectedSkills, $pokemonStored->skills);
      
      • 這段其實主要在第一行,這個method會去直接pokemons這張表是否有新增數據,
      • 但在技能的部分我想再多確認他是不是跟我原本創建的陣列一樣,所以第一行先不比對他,到第二行我實際取出已存入的技能資料,最後再和我一開始創建時候的技能做比對。

小結語

我這裡簡單介紹一下我是如何寫關於正確情況的featureTest,那有關於錯誤情況的,有機會下一篇再分享。


上一篇
Day23:寶可夢專案-自動化測試練習-Unit test VS Feature test
下一篇
Day25:寶可夢專案-學習寫測試-UnitTest實作
系列文
Laravel專案練習-寶可夢管理系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言